#include "stdafx.h"
#include "BSReader.h"
#include <assert.h>



#define JSON_ASSERT_UNREACHABLE assert( false )
#define JSON_ASSERT( condition ) assert( condition );  // @todo <= change this into an exception throw
#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) throw std::runtime_error( message );


static inline bool 
	in( BSReader::Char c, BSReader::Char c1, BSReader::Char c2, BSReader::Char c3, BSReader::Char c4 )
{
	return c == c1  ||  c == c2  ||  c == c3  ||  c == c4;
}

static inline bool 
	in( BSReader::Char c, BSReader::Char c1, BSReader::Char c2, BSReader::Char c3, BSReader::Char c4, BSReader::Char c5 )
{
	return c == c1  ||  c == c2  ||  c == c3  ||  c == c4  ||  c == c5;
}


static bool containsNewLine( BSReader::Location begin, BSReader::Location end )
{
	for ( ;begin < end; ++begin )
		if ( *begin == '\n'  ||  *begin == '\r' )
			return true;
	return false;
}

static std::string codePointToUTF8(unsigned int cp)
{
	std::string result;

	// based on description from http://en.wikipedia.org/wiki/UTF-8

	if (cp <= 0x7f) 
	{
		result.resize(1);
		result[0] = static_cast<char>(cp);
	} 
	else if (cp <= 0x7FF) 
	{
		result.resize(2);
		result[1] = static_cast<char>(0x80 | (0x3f & cp));
		result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
	} 
	else if (cp <= 0xFFFF) 
	{
		result.resize(3);
		result[2] = static_cast<char>(0x80 | (0x3f & cp));
		result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6)));
		result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12)));
	}
	else if (cp <= 0x10FFFF) 
	{
		result.resize(4);
		result[3] = static_cast<char>(0x80 | (0x3f & cp));
		result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
		result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
		result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
	}

	return result;
}


// Class Reader
// //////////////////////////////////////////////////////////////////

BSReader::BSReader(void)
{
}


BSReader::~BSReader(void)
{
}


bool BSReader::parse( const std::string &document, BSValue &root )
{
	document_ = document;
	const char *begin = document_.c_str();
	const char *end = begin + document_.length();
	return parse( begin, end, root);
}


bool BSReader::parse( std::istream& sin,BSValue &root )
{
	//std::istream_iterator<char> begin(sin);
	//std::istream_iterator<char> end;
	// Those would allow streamed input from a file, if parse() were a
	// template function.

	// Since std::string is reference-counted, this at least does not
	// create an extra copy.
	std::string doc;
	std::getline(sin, doc, (char)EOF);
	return parse( doc, root);
}

bool BSReader::parse( const char *beginDoc, const char *endDoc, BSValue &root )
{


	begin_ = beginDoc;
	end_ = endDoc;

	current_ = begin_;
	lastValueEnd_ = 0;
	lastValue_ = 0;


	while ( !nodes_.empty() )
		nodes_.pop();
	nodes_.push( &root );

	bool successful = readValue();


	if ( !root.isArray()  &&  !root.isObject() )
	{
		JSON_ASSERT_MESSAGE(false, "A valid JSON document must be either an array or an object value." );
		return false;
	}

	return successful;
}


bool BSReader::readValue()
{
	Token token;
	
	bool successful = true;

	readToken( token );


	switch ( token.type_ )
	{
	case tokenObjectBegin:
		successful = readObject( token );
		break;
	case tokenArrayBegin:
		successful = readArray( token );
		break;
	case tokenNumber:
		successful = decodeNumber( token );
		break;
	case tokenString:
		successful = decodeString( token );
		break;
	case tokenTrue:
		currentValue() = true;
		break;
	case tokenFalse:
		currentValue() = false;
		break;
	case tokenNull:
		currentValue() = BSValue();
		break;
	default:
		JSON_ASSERT_MESSAGE( false,"Syntax error: value, object or array expected." );
		return false;
	}

	return successful;
}

bool BSReader::expectToken( TokenType type, Token &token, const char *message )
{
	readToken( token );
	if ( token.type_ != type )
	{
		JSON_ASSERT_MESSAGE(false,message);
		return false;
	}
		
	return true;
}


bool BSReader::readToken( Token &token )
{
	skipSpaces();
	token.start_ = current_;
	Char c = getNextChar();
	bool ok = true;
	switch ( c )
	{
	case '{':
		token.type_ = tokenObjectBegin;
		break;
	case '}':
		token.type_ = tokenObjectEnd;
		break;
	case '[':
		token.type_ = tokenArrayBegin;
		break;
	case ']':
		token.type_ = tokenArrayEnd;
		break;
	case '"':
		token.type_ = tokenString;
		ok = readString();
		break;
	
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	case '-':
		token.type_ = tokenNumber;
		readNumber();
		break;
	case 't':
		token.type_ = tokenTrue;
		ok = match( "rue", 3 );
		break;
	case 'f':
		token.type_ = tokenFalse;
		ok = match( "alse", 4 );
		break;
	case 'n':
		token.type_ = tokenNull;
		ok = match( "ull", 3 );
		break;
	case ',':
		token.type_ = tokenArraySeparator;
		break;
	case ':':
		token.type_ = tokenMemberSeparator;
		break;
	case 0:
		token.type_ = tokenEndOfStream;
		break;
	default:
		ok = false;
		break;
	}
	if ( !ok )
		JSON_ASSERT_MESSAGE(false,"error");
	token.end_ = current_;
	return true;
}


void BSReader::skipSpaces()
{
	while ( current_ != end_ )
	{
		Char c = *current_;
		if ( c == ' '  ||  c == '\t'  ||  c == '\r'  ||  c == '\n' )
			++current_;
		else
			break;
	}
}


bool BSReader::match( Location pattern, 
	int patternLength )
{
	if ( end_ - current_ < patternLength )
		return false;
	int index = patternLength;
	while ( index-- )
		if ( current_[index] != pattern[index] )
			return false;
	current_ += patternLength;
	return true;
}








void BSReader::readNumber()
{
	while ( current_ != end_ )
	{
		if ( !(*current_ >= '0'  &&  *current_ <= '9')  &&
			!in( *current_, '.', 'e', 'E', '+', '-' ) )
			break;
		++current_;
	}
}

bool BSReader::readString()
{
	Char c = 0;
	while ( current_ != end_ )
	{
		c = getNextChar();
		if ( c == '\\' )
			getNextChar();
		else if ( c == '"' )
			break;
	}
	return c == '"';
}


bool BSReader::readObject( Token &tokenStart )
{
	Token tokenName;
	std::string name;
	currentValue() = BSValue( objectValue );
	while ( readToken( tokenName ) )
	{
		
		if ( tokenName.type_ == tokenObjectEnd  &&  name.empty() )  // empty object
			return true;
		if ( tokenName.type_ != tokenString )
			break;

		name = "";
		if ( !decodeString( tokenName, name ) )
		{
			JSON_ASSERT_MESSAGE(false,"error");
			return false;
		}
			
		
	
		Token colon;
		if ( !readToken( colon ) ||  colon.type_ != tokenMemberSeparator )
		{
			JSON_ASSERT_MESSAGE( false,"Missing ':' after object member name"  );
			return false;
		}
		BSValue &BSvalue = currentValue()[ name ];
		nodes_.push( &BSvalue );
		bool ok = readValue();
		nodes_.pop();
		if ( !ok ) // error already set
		{
			JSON_ASSERT_MESSAGE( false,"error"  );
			return false;
		}
			
		Token comma;
		if ( !readToken( comma )||  ( comma.type_ != tokenObjectEnd  &&  comma.type_ != tokenArraySeparator ) )
		{
			JSON_ASSERT_MESSAGE( false, "Missing ',' or '}' in object declaration" );
			return false;
		}
	
		
		if ( comma.type_ == tokenObjectEnd )
			return true;
	}
	JSON_ASSERT_MESSAGE( false, "Missing '}' or object member name" );
	return false;
}


bool BSReader::readArray( Token &tokenStart )
{
	currentValue() = BSValue( arrayValue );
	skipSpaces();
	if ( *current_ == ']' ) // empty array
	{
		Token endArray;
		readToken( endArray );
		return true;
	}
	int index = 0;
	while ( true )
	{
		BSValue &value = currentValue()[ index++ ];
		nodes_.push( &value );
		bool ok = readValue();
		nodes_.pop();
		if ( !ok ) // error already set
		{
			JSON_ASSERT_MESSAGE( false, "error" );
			return false;
		}

		Token token;
		// Accept Comment after last item in the array.
		ok = readToken( token );

		bool badTokenType = ( token.type_ == tokenArraySeparator  &&  
			token.type_ == tokenArrayEnd );
		if ( !ok  ||  badTokenType )
		{
			JSON_ASSERT_MESSAGE( false, "Missing ',' or ']' in array declaration" );
			return false;
		}
		
		if ( token.type_ == tokenArrayEnd )
			break;
	}
	return true;
}


bool BSReader::decodeNumber( Token &token )
{
	bool isDouble = false;
	for ( Location inspect = token.start_; inspect != token.end_; ++inspect )
	{
		isDouble = isDouble  
			||  in( *inspect, '.', 'e', 'E', '+' )  
			||  ( *inspect == '-'  &&  inspect != token.start_ );
	}
	if ( isDouble )
		return decodeDouble( token );
	Location current = token.start_;
	bool isNegative = *current == '-';
	if ( isNegative )
		++current;
	unsigned int threshold = (isNegative ? (unsigned int)(-BSValue::minInt) 
		: BSValue::maxUInt) / 10;
	unsigned int value = 0;
	while ( current < token.end_ )
	{
		Char c = *current++;
		if ( c < '0'  ||  c > '9' )
		{
			JSON_ASSERT_MESSAGE( false, std::string( token.start_, token.end_ ) + "' is not a number." );
			return false;
		}
			
		if ( value >= threshold )
			return decodeDouble( token );
		value = value * 10 + (unsigned int)(c - '0');
	}
	if ( isNegative )
		currentValue() = -(int)( value );
	else if ( value <= (unsigned int)(BSValue::maxInt) )
		currentValue() = (int)( value );
	else
		currentValue() = value;
	return true;
}


bool BSReader::decodeDouble( Token &token )
{
	double value = 0;
	const int bufferSize = 32;
	int count;
	int length = int(token.end_ - token.start_);
	if ( length <= bufferSize )
	{
		Char buffer[bufferSize];
		memcpy( buffer, token.start_, length );
		buffer[length] = 0;
		count = sscanf( buffer, "%lf", &value );
	}
	else
	{
		std::string buffer( token.start_, token.end_ );
		count = sscanf( buffer.c_str(), "%lf", &value );
	}

	if ( count != 1 )
	{
		JSON_ASSERT_MESSAGE( false,  "'" + std::string( token.start_, token.end_ ) + "' is not a number." );
		return false;
	}
	
	currentValue() = value;
	return true;
}


bool BSReader::decodeString( Token &token )
{
	std::string decoded;
	if ( !decodeString( token, decoded ) )
		return false;
	currentValue() = decoded;
	return true;
}


bool BSReader::decodeString( Token &token, std::string &decoded )
{
	decoded.reserve( token.end_ - token.start_ - 2 );
	Location current = token.start_ + 1; // skip '"'
	Location end = token.end_ - 1;      // do not include '"'
	while ( current != end )
	{
		Char c = *current++;
		if ( c == '"' )
			break;
		else if ( c == '\\' )
		{
			if ( current == end )
			{
				JSON_ASSERT_MESSAGE( false, "Empty escape sequence in string" );
				return false;
			}
			Char escape = *current++;
			switch ( escape )
			{
			case '"': decoded += '"'; break;
			case '/': decoded += '/'; break;
			case '\\': decoded += '\\'; break;
			case 'b': decoded += '\b'; break;
			case 'f': decoded += '\f'; break;
			case 'n': decoded += '\n'; break;
			case 'r': decoded += '\r'; break;
			case 't': decoded += '\t'; break;
			case 'u':
				{
					unsigned int unicode;
					if ( !decodeUnicodeCodePoint( token, current, end, unicode ) )
						return false;
					decoded += codePointToUTF8(unicode);
				}
				break;
			default:
				{
					JSON_ASSERT_MESSAGE( false, "Bad escape sequence in string");
					return false;
				}
				
			}
		}
		else
		{
			decoded += c;
		}
	}
	return true;
}

bool BSReader::decodeUnicodeCodePoint( Token &token, 
	Location &current, 
	Location end, 
	unsigned int &unicode )
{

	if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) )
		return false;
	if (unicode >= 0xD800 && unicode <= 0xDBFF)
	{
		// surrogate pairs
		if (end - current < 6)
		{
			JSON_ASSERT_MESSAGE( false, "additional six characters expected to parse unicode surrogate pair.");
			return false;
		}
		
		unsigned int surrogatePair;
		if (*(current++) == '\\' && *(current++)== 'u')
		{
			if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair ))
			{
				unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
			} 
			else
				return false;
		} 
		else
		{
			JSON_ASSERT_MESSAGE( false,  "expecting another \\u token to begin the second half of a unicode surrogate pair");
			return false;
		}
		
	}
	return true;
}

bool BSReader::decodeUnicodeEscapeSequence( Token &token, 
	Location &current, 
	Location end, 
	unsigned int &unicode )
{
	if ( end - current < 4 )
	{
		JSON_ASSERT_MESSAGE( false, "Bad unicode escape sequence in string: four digits expected.");
		return false;
	}
		
	unicode = 0;
	for ( int index =0; index < 4; ++index )
	{
		Char c = *current++;
		unicode *= 16;
		if ( c >= '0'  &&  c <= '9' )
			unicode += c - '0';
		else if ( c >= 'a'  &&  c <= 'f' )
			unicode += c - 'a' + 10;
		else if ( c >= 'A'  &&  c <= 'F' )
			unicode += c - 'A' + 10;
		else
		{
			JSON_ASSERT_MESSAGE( false,  "Bad unicode escape sequence in string: hexadecimal digit expected.");
			return false;
		}
	
	}
	return true;
}




BSValue &BSReader::currentValue()
{
	return *(nodes_.top());
}


BSReader::Char BSReader::getNextChar()
{
	if ( current_ == end_ )
		return 0;
	return *current_++;
}


void BSReader::getLocationLineAndColumn( Location location,
	int &line,
	int &column ) const
{
	Location current = begin_;
	Location lastLineStart = current;
	line = 0;
	while ( current < location  &&  current != end_ )
	{
		Char c = *current++;
		if ( c == '\r' )
		{
			if ( *current == '\n' )
				++current;
			lastLineStart = current;
			++line;
		}
		else if ( c == '\n' )
		{
			lastLineStart = current;
			++line;
		}
	}
	// column & line start at 1
	column = int(location - lastLineStart) + 1;
	++line;
}


std::string BSReader::getLocationLineAndColumn( Location location ) const
{
	int line, column;
	getLocationLineAndColumn( location, line, column );
	char buffer[18+16+16+1];
	sprintf( buffer, "Line %d, Column %d", line, column );
	return buffer;
}




std::istream& operator>>( std::istream &sin, BSValue &root )
{
	BSReader reader;
	bool ok = reader.parse(sin, root);
	JSON_ASSERT( ok );
	 
	return sin;
}


 // namespace Json
